package code;

import java.io.*;
import java.util.*;

import math.*;
import model.MF;


import data.*;
import file.Parser;

public class OrdinalClassifierFixed_Clone2 {
	
	public static void main(String[] arg) throws IOException {

		System.out.println("Program started...");
		int nrat = 3298163;
		int nuser = 93705;
		int nitem = 3561;
		int maxuid = 95526;
		
		int[] usumrat = new int[maxuid];
		for(int i=0; i<maxuid; i++) {
			usumrat[i] = 0;
		}		
		int[] isumrat = new int[nitem];
		for(int i=0; i<nitem; i++) {
			isumrat[i] = 0;
		}
		
		int[] unrat = new int[maxuid];
		for(int i=0; i<maxuid; i++) {
			unrat[i] = 0;
		}
		int[] inrat = new int[nitem];
		for(int i=0; i<nitem; i++) {
			inrat[i] = 0;
		}
		
		List<Data> traintest = new ArrayList<Data>();		
		
		List<Data> train = new ArrayList<Data>();
		String dir = "F:\\Users\\thap\\svm_light_windows (1)\\ordinal_classifier\\";
		int id = 0;

		int urank = 1;
		BufferedReader br = new BufferedReader(new FileReader("F:\\Users\\thap\\data\\smalltrain"));
		String line=null;
		double sumRat = 0.0;
		
		while((line = br.readLine()) != null) {
			String[] fields = line.split(",");
			int uid = Integer.parseInt(fields[0]);
			short iid = Short.parseShort(fields[1]);
			byte rat = Byte.parseByte(fields[2]);	
			
			train.add(new Data(uid,iid,rat));	
			traintest.add(new Data(uid,iid,rat));
			
			usumrat[uid-1] += rat;
			isumrat[iid-1] += rat;
			
			unrat[uid-1]++;
			inrat[iid-1]++;
			
			sumRat += rat;
			
			id++;
		}
		br.close();
		System.out.println("Number of ratings in training set: " + train.size());
		
		List<Data> test = new ArrayList<Data>();
		BufferedReader br2 = new BufferedReader(new FileReader("F:\\Users\\thap\\data\\smallvalidate"));
		line=null;
		
		while((line = br2.readLine()) != null) {
			String[] fields = line.split(",");
			int uid = Integer.parseInt(fields[0]);
			short iid = Short.parseShort(fields[1]);
			byte rat = Byte.parseByte(fields[2]);	
			
			test.add(new Data(uid,iid,rat));	
			traintest.add(new Data(uid,iid,rat));
			
			sumRat += rat;
		}
		br2.close();
		double avgrat = sumRat / (double) (train.size()+test.size());
		System.out.println("Number of ratings in test set: " + test.size());
		System.out.println("Avg. ratings: " + avgrat);
		
		double[] ubias = new double[maxuid];
		for(int i=0; i<maxuid; i++) {
			ubias[i] = 0;
			if (unrat[i] != 0) {
				ubias[i] = 0;
			}
		}
		double[] ibias = new double[nitem];
		for(int i=0; i<nitem; i++) {
			ibias[i] = 0.0;
			if (inrat[i] != 0) {
				ibias[i] = 0;
			}
		}
		
		byte k = 5;
		
		double[] ufactor = new double[maxuid * k];
		double[] ifactor = new double[nitem * k];	
		
		//set factor randomly
		for(int i=0; i<maxuid*k; i++) {
			ufactor[i] = (Math.random()-0.5)/k;
		}
		for(int i=0; i<nitem*k; i++) {
			ifactor[i] = (Math.random()-0.5)/k;
		}
		
		double gamma = (double) 0.02;
		double lambda =(double) 0.02;
		
		double epsilon = (double) 0.05;
		
		Writer writer1 = new BufferedWriter(new FileWriter(dir+"train005"));	
		
		Writer writer3 = new BufferedWriter(new FileWriter(dir+"train005_"));
		
		Writer writerTrainData = new BufferedWriter(new FileWriter(dir+"smalltrain005_data"));
		Writer writerTrainSign = new BufferedWriter(new FileWriter(dir+"smalltrain005_sign"));
		
		
		System.out.println("Dimensionality k = " + k);
		System.out.println("Gamma = " + gamma);
		System.out.println("Lambda = " + lambda);
		System.out.println("Epsilon = "+epsilon);
		
		//initial error
		System.out.println("Initial training RMSE: " + RMSE.RMSE(train, avgrat, ubias, ibias, ufactor, ifactor, k));
		
		//learn
		for (int it=1;it<=100;it++){
			double formerrmse = RMSE.RMSE(traintest, avgrat, ubias, ibias, ufactor, ifactor, k);
			for(int i=0; i<traintest.size(); i++) {
				int uid = traintest.get(i).uid;
				short iid = traintest.get(i).iid;
				byte rat = traintest.get(i).rat;

				double pred = 0.0;
				pred += avgrat;
				pred += ubias[uid-1];
				pred += ibias[iid-1];
				for(int j=0; j<k; j++) {
					pred += ufactor[(uid-1)*k+j]*ifactor[(iid-1)*k+j];
				}
				double err = rat - pred;
				
				ubias[uid-1] =  ubias[uid-1] + gamma*(err-lambda*ubias[uid-1]);
				ibias[iid-1] =  ibias[iid-1] + gamma*(err-lambda*ibias[iid-1]);
				
				for(int j=0; j<k; j++) {
					double newufactor = ufactor[(uid-1)*k+j] + gamma*(err*ifactor[(iid-1)*k+j] - lambda*ufactor[(uid-1)*k+j]);
					double newifactor = ifactor[(iid-1)*k+j] + gamma*(err*ufactor[(uid-1)*k+j] - lambda*ifactor[(iid-1)*k+j]);
					ufactor[(uid-1)*k+j] = newufactor;
					ifactor[(iid-1)*k+j] = newifactor;
				}
			}
			double latterrmse = RMSE.RMSE(traintest, avgrat, ubias, ibias, ufactor, ifactor, k);
			if (formerrmse - latterrmse < 0.0001){
				System.out.println("Converged at step " + it);
				break;
			}
		}
		
		//error after learning
		System.out.println("Training RMSE after learning: " + RMSE.RMSE(train, avgrat, ubias, ibias, ufactor, ifactor, k));
		
		// svmlearn input
		List<Data> postraintest = new ArrayList<Data>();
		List<Data> negtraintest = new ArrayList<Data>();
		List<Data> postrain = new ArrayList<Data>();
		List<Data> negtrain = new ArrayList<Data>();
		List<Data> ztrain = new ArrayList<Data>();
		
		for(int i=0; i<train.size(); i++){
			int uid = train.get(i).uid;
			short iid = train.get(i).iid;
			byte rat = train.get(i).rat;
			double pred = avgrat + ubias[uid-1] + ibias[iid-1];
			
			if(rat - pred - epsilon > 0) {
				postraintest.add(train.get(i));
			}
			else if(rat - pred + epsilon < 0) {
				negtraintest.add(train.get(i));
			}
			else {
				ztrain.add(train.get(i));
			}
		}
		
		double[] uposbias = new double[maxuid];
		double[] iposbias = new double[nitem];
		
		double[] uposfactor = new double[maxuid * k];
		double[] iposfactor = new double[nitem * k];
		
		double[] unegbias = new double[maxuid];
		double[] inegbias = new double[nitem];
		
		double[] unegfactor = new double[maxuid * k];
		double[] inegfactor = new double[nitem * k];
		
		for(int i=0; i<maxuid; i++) {
			uposbias[i] = 0;
			unegbias[i] = 0;
			for(int j=0; j<k; j++){
				uposfactor[i*k+j] = (Math.random()-0.5)/(double)k;
				unegfactor[i*k+j] = (Math.random()-0.5)/(double)k;
			}
		}
		
		for(int i=0; i<nitem; i++) {
			iposbias[i] = 0;
			inegbias[i] = 0;
			for(int j=0; j<k; j++){
				iposfactor[i*k+j] = (Math.random()-0.5)/(double)k;
				inegfactor[i*k+j] = (Math.random()-0.5)/(double)k;
			}
		}
		
		//find average positive, negative rating
		double sumposrat = 0.0;
		double sumnegrat = 0.0;
		
		for(Data data : postraintest) {
			sumposrat += data.rat;
		}
		for(Data data : negtraintest) {
			sumnegrat += data.rat;
		}
		
		double posavgrat = sumposrat/postraintest.size();
		double negavgrat = sumnegrat/negtraintest.size();
		System.out.println("Average positive rating: " + posavgrat);
		System.out.println("Average negative rating: " + negavgrat);
		
		//learn
		for (int it=1;it<=100;it++){
			double formerrmse = RMSE.RMSE(postraintest, posavgrat, uposbias, iposbias, uposfactor, iposfactor, k);
			for(Data data : postraintest) {
				int uid = data.uid;
				short iid = data.iid;
				byte rat = data.rat;

				double pred = 0.0;
				pred += posavgrat;
				pred += uposbias[uid-1];
				pred += iposbias[iid-1];
				for(int j=0; j<k; j++) {
					pred += uposfactor[(uid-1)*k+j]*iposfactor[(iid-1)*k+j];
				}
				double err = rat - pred;
				
				uposbias[uid-1] =  uposbias[uid-1] + gamma*(err-lambda*uposbias[uid-1]);
				iposbias[iid-1] =  iposbias[iid-1] + gamma*(err-lambda*iposbias[iid-1]);
				
				for(int j=0; j<k; j++) {
					double newufactor = uposfactor[(uid-1)*k+j] + gamma*(err*iposfactor[(iid-1)*k+j] - lambda*uposfactor[(uid-1)*k+j]);
					double newifactor = iposfactor[(iid-1)*k+j] + gamma*(err*uposfactor[(uid-1)*k+j] - lambda*iposfactor[(iid-1)*k+j]);
					uposfactor[(uid-1)*k+j] = newufactor;
					iposfactor[(iid-1)*k+j] = newifactor;
				}
			}
			double latterrmse = RMSE.RMSE(postraintest, posavgrat, uposbias, iposbias, uposfactor, iposfactor, k);
			if (formerrmse - latterrmse < 0.0001){
				System.out.println("Positive training converged at step " + it);
				break;
			}
		}
		for (int it=1;it<=100;it++){
			double formerrmse = RMSE.RMSE(negtraintest, negavgrat, unegbias, inegbias, unegfactor, inegfactor, k);
			for(Data data : negtraintest) {
				int uid = data.uid;
				short iid = data.iid;
				byte rat = data.rat;

				double pred = 0.0;
				pred += negavgrat;
				pred += unegbias[uid-1];
				pred += inegbias[iid-1];
				for(int j=0; j<k; j++) {
					pred += unegfactor[(uid-1)*k+j]*inegfactor[(iid-1)*k+j];
				}
				double err = rat - pred;
				
				unegbias[uid-1] =  unegbias[uid-1] + gamma*(err-lambda*unegbias[uid-1]);
				inegbias[iid-1] =  inegbias[iid-1] + gamma*(err-lambda*inegbias[iid-1]);
				
				for(int j=0; j<k; j++) {
					double newufactor = unegfactor[(uid-1)*k+j] + gamma*(err*inegfactor[(iid-1)*k+j] - lambda*unegfactor[(uid-1)*k+j]);
					double newifactor = inegfactor[(iid-1)*k+j] + gamma*(err*unegfactor[(uid-1)*k+j] - lambda*inegfactor[(iid-1)*k+j]);
					unegfactor[(uid-1)*k+j] = newufactor;
					inegfactor[(iid-1)*k+j] = newifactor;
				}
			}
			double latterrmse = RMSE.RMSE(negtraintest, negavgrat, unegbias, inegbias, unegfactor, inegfactor, k);
			if (formerrmse - latterrmse < 0.0001){
				System.out.println("Negative training converged at step " + it);
				break;
			}
		}
		
		List<Data> posexamples = new ArrayList<Data>();
		List<Data> negexamples = new ArrayList<Data>();
		List<Data> neuexamples = new ArrayList<Data>();
		
		for (Data data : train){
			int uid = data.uid;
			short iid = data.iid;
			byte rat = data.rat;
			
			double pred = avgrat + ubias[uid-1]+ibias[iid-1];
			
			double dot = 0.0;
			for(int i=0; i<k; i++){
				dot += ufactor[(uid-1)*k+i] * ifactor[(iid-1)*k+i];
			}
			
			if ( rat - pred - epsilon > 0){// && dot > 0 ){
				posexamples.add(data);
			}
			
			else if (rat - pred + epsilon < 0){// && dot < 0 ){
				negexamples.add(data);
			}
			
			else {
				neuexamples.add(data);
			}
		}
		
		//System.out.println("Number of pos. ratings in training: "+(100 * posexamples.size()/(double)train.size()));
		
		//System.out.println("Number of neu. ratings in training: "+(100 * neuexamples.size()/(double)train.size()));
		
		//System.out.println("Number of neg. ratings in training: "+(100 * negexamples.size()/(double)train.size()));
		
		System.out.println("Percentage of pos. ratings in training: "+(100 * posexamples.size()/(double)train.size()));
		
		System.out.println("Percentage of neu. ratings in training: "+(100 * neuexamples.size()/(double)train.size()));
		
		System.out.println("Percentage of neg. ratings in training: "+(100 * negexamples.size()/(double)train.size()));
		
		int npostrain = posexamples.size();
		int nztrain = neuexamples.size();
		int nnegtrain = negexamples.size();
		
		Random random = new Random();
		
		List<Data> trainSVM = new ArrayList<Data>();

		for(int i=0; i < npostrain/10; i++){
			int rand = random.nextInt(npostrain);
			Data data = posexamples.get(rand);
			if(!trainSVM.contains(data)){
				trainSVM.add(data);
			}
			int uid = data.uid;
			short iid = data.iid;
			writer1.write("+1 ");
			double dot = 0.0;
			for(int j=0; j<k; j++){
				dot += ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j];
				writer1.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			double dot2 = 0.0;
			for(int j=0; j<k; j++){
				dot2 += uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j];
				writer1.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			double dot3 = 0.0;
			for(int j=0; j<k; j++){
				dot3 += unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j];
				writer1.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer1.write((1+9*k)+":"+dot+" ");
			writer1.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer1.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer1.write((4+9*k)+":"+dot2+" ");
			writer1.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer1.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer1.write((7+9*k)+":"+dot3+" ");
			writer1.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer1.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer1.write("\n");
			
			writer3.write("-1 ");
			for(int j=0; j<k; j++){
				writer3.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer3.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer3.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer3.write((1+9*k)+":"+dot+" ");
			writer3.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer3.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer3.write((4+9*k)+":"+dot2+" ");
			writer3.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer3.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer3.write((7+9*k)+":"+dot3+" ");
			writer3.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer3.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer3.write("\n");	
		}	

		for(int i=0; i < nztrain/10; i++){
			int rand = random.nextInt(nztrain);
			Data data = neuexamples.get(rand);
			if(!trainSVM.contains(data)){
				trainSVM.add(data);
			}
			int uid = data.uid;
			short iid = data.iid;
			writer1.write("-1 ");
			double dot = 0.0;
			for(int j=0; j<k; j++){
				dot += ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j];
				writer1.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			double dot2 = 0.0;
			for(int j=0; j<k; j++){
				dot2 += uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j];
				writer1.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			double dot3 = 0.0;
			for(int j=0; j<k; j++){
				dot3 += unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j];
				writer1.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer1.write((1+9*k)+":"+dot+" ");
			writer1.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer1.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer1.write((4+9*k)+":"+dot2+" ");
			writer1.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer1.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer1.write((7+9*k)+":"+dot3+" ");
			writer1.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer1.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer1.write("\n");
			
			writer3.write("-1 ");
			for(int j=0; j<k; j++){
				writer3.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer3.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer3.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer3.write((1+9*k)+":"+dot+" ");
			writer3.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer3.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer3.write((4+9*k)+":"+dot2+" ");
			writer3.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer3.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer3.write((7+9*k)+":"+dot3+" ");
			writer3.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer3.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer3.write("\n");			
		}

		for(int i=0; i < nnegtrain/10; i++){
			int rand = random.nextInt(nnegtrain);
			Data data = negexamples.get(rand);
			if(!trainSVM.contains(data)){
				trainSVM.add(data);
			}
			int uid = data.uid;
			short iid = data.iid;
			writer1.write("-1 ");
			double dot = 0.0;
			for(int j=0; j<k; j++){
				dot += ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j];
				writer1.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			double dot2 = 0.0;
			for(int j=0; j<k; j++){
				dot2 += uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j];
				writer1.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			double dot3 = 0.0;
			for(int j=0; j<k; j++){
				dot3 += unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j];
				writer1.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer1.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer1.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer1.write((1+9*k)+":"+dot+" ");
			writer1.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer1.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer1.write((4+9*k)+":"+dot2+" ");
			writer1.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer1.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer1.write((7+9*k)+":"+dot3+" ");
			writer1.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer1.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer1.write("\n");
			
			writer3.write("+1 ");
			for(int j=0; j<k; j++){
				writer3.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer3.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer3.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer3.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer3.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer3.write((1+9*k)+":"+dot+" ");
			writer3.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer3.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer3.write((4+9*k)+":"+dot2+" ");
			writer3.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer3.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer3.write((7+9*k)+":"+dot3+" ");
			writer3.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer3.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer3.write("\n");		
		}
		
		writer1.close();
		
		writer3.close();
		
		for(Data data : posexamples){
			writerTrainData.write(data.uid+","+data.iid+","+data.rat+"\n");
			writerTrainSign.write("1\n");
		}
		
		for(Data data : neuexamples){
			writerTrainData.write(data.uid+","+data.iid+","+data.rat+"\n");
			writerTrainSign.write("0\n");
		}
		
		for(Data data : negexamples){
			writerTrainData.write(data.uid+","+data.iid+","+data.rat+"\n");
			writerTrainSign.write("-1\n");
		}
		
		writerTrainData.close();
		writerTrainSign.close();
		
		List<Data> postest = new ArrayList<Data>();
		List<Data> negtest = new ArrayList<Data>();
		List<Data> neutest = new ArrayList<Data>();
		
		for(Data data : test){
			int uid = data.uid;
			short iid = data.iid;
			byte rat = data.rat;
			
			double pred = avgrat + ubias[uid-1] + ibias[iid-1];
			
			double dot = 0;
			for(int i=0; i<k; i++){
				dot += ufactor[(uid-1)*k+i]+ifactor[(iid-1)*k+i];
			}
			
			if( rat - pred - epsilon > 0 ) {
				postest.add(data);
			}
			
			else if ( rat - pred + epsilon < 0 ){
				negtest.add(data);
			}
			else {
				neutest.add(data);
			}
		}
		
		System.out.println("Number of pos. ratings in test: "+postest.size());
		
		System.out.println("Number of neu. ratings in test: "+neutest.size());
		
		System.out.println("Number of neg. ratings in test: "+negtest.size());
		
		System.out.println("Percentage of pos. ratings in test: "+(100 * postest.size()/(double)test.size()));
		
		System.out.println("Percentage of neu. ratings in test: "+(100 * neutest.size()/(double)test.size()));
		
		System.out.println("Percentage of neg. ratings in test: "+(100 * negtest.size()/(double)test.size()));
		
		Writer writer2 = new BufferedWriter(new FileWriter(dir+"test005"));
		
		Writer writer4 = new BufferedWriter(new FileWriter(dir+"test005_"));
		
		Writer writerTestData = new BufferedWriter(new FileWriter(dir+"smalltest005_data"));
		
		Writer writerTestSign = new BufferedWriter(new FileWriter(dir+"smalltest005_sign"));
		
		for(int i=0; i < postest.size(); i++){
			int uid = postest.get(i).uid;
			short iid = postest.get(i).iid;
			byte rat = postest.get(i).rat;
			
			writer2.write("+1 ");
			double dot = 0.0;
			for(int j=0; j<k; j++){
				dot += ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j];
				writer2.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			double dot2 = 0.0;
			for(int j=0; j<k; j++){
				dot2 += uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j];
				writer2.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			double dot3 = 0.0;
			for(int j=0; j<k; j++){
				dot3 += unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j];
				writer2.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer2.write((1+9*k)+":"+dot+" ");
			writer2.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer2.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer2.write((4+9*k)+":"+dot2+" ");
			writer2.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer2.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer2.write((7+9*k)+":"+dot3+" ");
			writer2.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer2.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer2.write("\n");					

			writer4.write("-1 ");
			for(int j=0; j<k; j++){
				writer4.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer4.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer4.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer4.write((1+9*k)+":"+dot+" ");
			writer4.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer4.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer4.write((4+9*k)+":"+dot2+" ");
			writer4.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer4.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer4.write((7+9*k)+":"+dot3+" ");
			writer4.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer4.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer4.write("\n");
		}
		
		for(int i=0; i < neutest.size(); i++){
			int uid = neutest.get(i).uid;
			short iid = neutest.get(i).iid;
			byte rat = neutest.get(i).rat;
			
			writer2.write("-1 ");
			double dot = 0.0;
			for(int j=0; j<k; j++){
				dot += ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j];
				writer2.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			double dot2 = 0.0;
			for(int j=0; j<k; j++){
				dot2 += uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j];
				writer2.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			double dot3 = 0.0;
			for(int j=0; j<k; j++){
				dot3 += unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j];
				writer2.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer2.write((1+9*k)+":"+dot+" ");
			writer2.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer2.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer2.write((4+9*k)+":"+dot2+" ");
			writer2.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer2.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer2.write((7+9*k)+":"+dot3+" ");
			writer2.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer2.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer2.write("\n");				

			writer4.write("-1 ");
			for(int j=0; j<k; j++){
				writer4.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer4.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer4.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer4.write((1+9*k)+":"+dot+" ");
			writer4.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer4.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer4.write((4+9*k)+":"+dot2+" ");
			writer4.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer4.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer4.write((7+9*k)+":"+dot3+" ");
			writer4.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer4.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer4.write("\n");	
		}
		
		for(int i=0; i < negtest.size(); i++){
			int uid = negtest.get(i).uid;
			short iid = negtest.get(i).iid;
			byte rat = negtest.get(i).rat;
			
			writer2.write("-1 ");
			double dot = 0.0;
			for(int j=0; j<k; j++){
				dot += ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j];
				writer2.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			double dot2 = 0.0;
			for(int j=0; j<k; j++){
				dot2 += uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j];
				writer2.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			double dot3 = 0.0;
			for(int j=0; j<k; j++){
				dot3 += unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j];
				writer2.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer2.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer2.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer2.write((1+9*k)+":"+dot+" ");
			writer2.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer2.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer2.write((4+9*k)+":"+dot2+" ");
			writer2.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer2.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer2.write((7+9*k)+":"+dot3+" ");
			writer2.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer2.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer2.write("\n");		

			writer4.write("+1 ");
			for(int j=0; j<k; j++){
				writer4.write((1+3*j)+":"+ufactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j)+":"+ifactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j)+":"+(ufactor[(uid-1)*k+j] * ifactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer4.write((1+3*j+3*k)+":"+uposfactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j+3*k)+":"+iposfactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j+3*k)+":"+(uposfactor[(uid-1)*k+j] * iposfactor[(iid-1)*k+j])+" ");
			}
			for(int j=0; j<k; j++){
				writer4.write((1+3*j+6*k)+":"+unegfactor[(uid-1)*k+j]+" ");
				writer4.write((2+3*j+6*k)+":"+inegfactor[(iid-1)*k+j]+" ");
				writer4.write((3+3*j+6*k)+":"+(unegfactor[(uid-1)*k+j] * inegfactor[(iid-1)*k+j])+" ");
			}
			writer4.write((1+9*k)+":"+dot+" ");
			writer4.write((2+9*k)+":"+ubias[uid-1]+" ");
			writer4.write((3+9*k)+":"+ibias[iid-1]+" ");
			writer4.write((4+9*k)+":"+dot2+" ");
			writer4.write((5+9*k)+":"+uposbias[uid-1]+" ");
			writer4.write((6+9*k)+":"+iposbias[iid-1]+" ");
			writer4.write((7+9*k)+":"+dot3+" ");
			writer4.write((8+9*k)+":"+unegbias[uid-1]+" ");
			writer4.write((9+9*k)+":"+inegbias[iid-1]+" ");
			writer4.write("\n");			
		}
		
		for(int i=0; i < postest.size(); i++){
			int uid = postest.get(i).uid;
			short iid = postest.get(i).iid;
			byte rat = postest.get(i).rat;
			
			writerTestData.write(uid+","+iid+","+rat+"\n");
			writerTestSign.write("1\n");
		}
		
		for(int i=0; i < neutest.size(); i++){
			int uid = neutest.get(i).uid;
			short iid = neutest.get(i).iid;
			byte rat = neutest.get(i).rat;	
			
			writerTestData.write(uid+","+iid+","+rat+"\n");	
			writerTestSign.write("0\n");
		}
		
		for(int i=0; i < negtest.size(); i++){
			int uid = negtest.get(i).uid;
			short iid = negtest.get(i).iid;
			byte rat = negtest.get(i).rat;
			
			writerTestData.write(uid+","+iid+","+rat+"\n");
			writerTestSign.write("-1\n");
		}
			
		
		writer2.close();
		writer4.close();
		writerTestData.close();
		writerTestSign.close();
		
		Set<Integer> userSet = new HashSet<Integer>();
		Set<Short> itemSet = new HashSet<Short>();
		
		for(Data data : trainSVM){
			int uid = data.uid;
			short iid = data.iid;
			
			userSet.add(uid);
			itemSet.add(iid);
		}
		int ufail=0;
		int ifail=0;
		for(Data data : test){
			int uid = data.uid;
			short iid = data.iid;
			
			if(!userSet.contains(uid)){
				ufail++;
			}
			if(!itemSet.contains(iid)){
				ifail++;
			}
		}
		
		Set<Integer> userTSet = new HashSet<Integer>();
		Set<Short> itemTSet = new HashSet<Short>();
		
		for(Data data : test){
			int uid = data.uid;
			short iid = data.iid;
			
			userTSet.add(uid);
			itemTSet.add(iid);
		}
		
		System.out.println("num user in test not included in training set "+ufail);
		System.out.println("num item in test not included in training set "+ifail);
		System.out.println("num user in test set "+userTSet.size());
		System.out.println("num item in test set "+itemTSet.size());
		System.out.println("num user in training set "+userSet.size());
		System.out.println("num item in training set "+itemSet.size());
	}
}
